/* 
*  the engineUtility is to provide utilities for Trans Service
*  
*  configuration: read configurations for certain site
*  
*  call Trnas Service: based on configuration, service name and params to provide result
*
*/

var BASE64_KEYSTR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

/*
* base64 encode
*/
function encode64(input) {
  var output = "";
  var chr1, chr2, chr3 = "";
  var enc1, enc2, enc3, enc4 = "";
  var i = 0;

  do {
     chr1 = input.charCodeAt(i++);
     chr2 = input.charCodeAt(i++);
     chr3 = input.charCodeAt(i++);

     enc1 = chr1 >> 2;
     enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
     enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
     enc4 = chr3 & 63;

     if (isNaN(chr2)) {
        enc3 = enc4 = 64;
     } else if (isNaN(chr3)) {
        enc4 = 64;
     }

     output = output + 
        BASE64_KEYSTR.charAt(enc1) + 
        BASE64_KEYSTR.charAt(enc2) + 
        BASE64_KEYSTR.charAt(enc3) + 
        BASE64_KEYSTR.charAt(enc4);
     chr1 = chr2 = chr3 = "";
     enc1 = enc2 = enc3 = enc4 = "";
  } while (i < input.length);

  return output;
}

/*
* base64 decode
*/
function decode64(input) {
  var output = "";
  var chr1, chr2, chr3 = "";
  var enc1, enc2, enc3, enc4 = "";
  var i = 0;
      
      if(input.length%4!=0)
      {
                return "";
      }
  var base64test = /[^A-Za-z0-9\+\/\=]/g;
      if (base64test.exec(input))
      {
                return "";
      }

  do {
     enc1 = BASE64_KEYSTR.indexOf(input.charAt(i++));
     enc2 = BASE64_KEYSTR.indexOf(input.charAt(i++));
     enc3 = BASE64_KEYSTR.indexOf(input.charAt(i++));
     enc4 = BASE64_KEYSTR.indexOf(input.charAt(i++));

     chr1 = (enc1 << 2) | (enc2 >> 4);
     chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
     chr3 = ((enc3 & 3) << 6) | enc4;
             
             output = output + String.fromCharCode(chr1);

     if (enc3 != 64) {
                    output+=String.fromCharCode(chr2);
     }
     if (enc4 != 64) {
                    output+=String.fromCharCode(chr3);
     }

     chr1 = chr2 = chr3 = "";
     enc1 = enc2 = enc3 = enc4 = "";

  } while (i < input.length);

  return output;
}
   

// Find text between a start and end text
function find_delimited(search_str, start_str, end_str, in_index) {
    var found_txt = null;
    var j = -1;
    var i = search_str.indexOf(start_str, in_index);
    if (i>=0) {
        j = search_str.indexOf(end_str, i+start_str.length);
        if (j>0) {
            found_txt = search_str.substring(i+start_str.length, j);
        }
    }
	//logInfo("view i, j " +  i+ "   " +j +"   "+ search_str.indexOf(end_str, i+start_str.length) + "  " +start_str.length+ "   "+end_str);
    return {result:found_txt,index: j};
}

/*
* only for flat json 1 layer
*/
function json2form(sourcejson){
var result_txt = "";
  for(var attr in sourcejson) {
  
	  //"++++" is used to generate the params with duplicated attribute names
	  attr_name = ""+attr;
	  if( attr_name.indexOf("<<<<") >= 0 ) {
		var temp_result = find_delimited(attr_name,"<<<<",">>>>", 0);
		if(temp_result.index >= 0 ) attr_name = temp_result.result;
	  }
      if(result_txt.length > 0 ) {
         result_txt = result_txt + "&" + attr_name + "=" + sourcejson[attr];
      }else{
         result_txt = attr_name + "=" + sourcejson[attr];
      }
  }
  logInfo(" json2form " + result_txt);
  return result_txt;
}

/*
* to load configuration obj based on config_filename
* call palm service to get the data
*/
function getConfiguration(config_filename, in_controller,f) {

   
    logInfo("config_filename : "+ config_filename);

    in_controller.serviceRequest("palm://com.hpit.cdc.pagetrans.service", 
        {
            method: "process",
            parameters: {'configfile': config_filename},
            onSuccess:function(successData) {
                        f.result = successData;
                       }.bind(this),
            onFailure:function(failureData) {
                        f.result = null;
                       }.bind(this),
        });

}

/*
* to load configuration obj based on config_filename
* call file import lib to get configuration in Chrome...
*/
function getConfiguration4Chrome(config_filename,f) {

   
    logInfo("config_filename : "+ config_filename);
	
	f.result = initiation(config_filename);

    /*in_controller.serviceRequest("palm://com.hpit.cdc.pagetrans.service", 
        {
            method: "process",
            parameters: {'configfile': config_filename},
            onSuccess:function(successData) {
                        f.result = successData;
                       }.bind(this),
            onFailure:function(failureData) {
                        f.result = null;
                       }.bind(this),
        });
	*/
}

/*
* wrapper of Mojo logInfo
*/
function logInfo(msg) {
    Mojo.Log.info(msg);
}


/*
* get the url http response with params
*/
function get_url_response(source_url, in_params, methods, in_future) {

   
    var options = { "bodyEncoding":"utf8"};
    
    //for debug
    logInfo("source_url, methods " + source_url + "||||" +methods);
    
    var requestHeaders = "";
    //get request header from the json object in_params
    if( in_params['__requestHeaders'] ) {
      requestHeaders = in_params['__requestHeaders'];
      delete in_params['__requestHeaders'];
    }
    
    logInfo(" in_params "+JSON.stringify(in_params) + " requestHeaders   " + JSON.stringify(requestHeaders) );
    
	if(methods == "get") {
	  source_url = source_url + "?" + encodeURI(json2form(in_params));
	}
    
    var request = new Ajax.Request(source_url, {
			'method': methods,
			'evalJSON': 'false',
			'contentType': 'application/x-www-form-urlencoded',
			'postBody': json2form(in_params),
			'requestHeaders': requestHeaders,
			onSuccess: function(transport){ // If page is returned
					logInfo("in onSuccess handler");
					logInfo("Server: " + transport.getResponseHeader("Server"));
					logInfo("Date: " + transport.getResponseHeader("Date"));
					logInfo("Status: " + transport.getStatus());
					logInfo("Content-Length: " + transport.getResponseHeader("Content-Length"));
					
					// Looks for email in response text (delimited by starting and ending text)
					var response = transport.responseText || "no response text";
					//logInfo("Response: "+JSON.stringify(response));
									
					//for test
					//var result11 = find_delimited(response, "Authorization", ">")
					//logInfo(" find auth :" +response.substring(result11.index - 40, response.length));
					
					in_future.result = response;
			}.bind(this),
			
			on0: function(transport){ // If connection problem
				logInfo("In on0 handler");
				logInfo("Transport: " + JSON.stringify(transport));
				logInfo("Status: " + transport.getStatus());
//				this.controller.showAlertDialog({
//					title: "Connection error",
//					message: "Unable to connect to sign-in service",
//					choices: [{
//						label: "OK",
//						type: 'dismiss'
//					}, ]
//				});
				in_future.result = 'failed';
			}.bind(this),
			
			on401: function(transport){ // If connection problem
//				this.controller.showAlertDialog({
//					title: "Sign-in error",
//					message: "Sign-in failed. Check user name and password",
//					choices: [{
//						label: "OK",
//						type: 'dismiss'
//					}, ]
//				});
				in_future.result = 'failed';
			}.bind(this),

			onFailure: function(transport){ // In case of other failure (with a failed status)
				logInfo("in onFailure handler");
				logInfo("Status: " + transport.getStatus());
//				this.controller.showAlertDialog({
//					title: "Sign-in error",
//					message: "Sign-in failed with status "+transport.getStatus(),
//					choices: [{
//						label: "OK",
//						type: 'dismiss'
//					}, ]
//				});
				in_future.result = 'failed';
			}.bind(this)
		});    
}


/*
* call the page service, wrapper function to wrap everything
*/
function callTransService(config_obj, in_servicename, in_params, in_future)
{
  //get configObj from palm service...
  
  //get pagelist
  var page_list = config_obj.page_list;
  
  logInfo("in_servicename = " +in_servicename + " page_list =" + JSON.stringify(page_list));
  var result_json_string = "{";
  
  var result = null;
  var i = 0;
  
  while ( i < page_list.length && result == null ) {
    if (page_list[i].service_name == in_servicename ) {
        result = page_list[i];
    }else{
        i++;
    }  
  }//end of while
  
  var values = [];
 
  logInfo("servicename/result " + in_servicename+"/"+JSON.stringify(result));
  
  var response_content = null;
  m_code = 'ok';
  
  //result contains the service parameters, the link and patterns
  if (result != null) { 
    //get link
    
    //call the web server, use in_future to control the flow
    var f = new Future();
    
    var params = result.params;
    for(var attr in in_params){//
        params[attr] = in_params[attr];
    }
    
    logInfo( "params = " + JSON.stringify(params) );
   
    //call the url to get response
    get_url_response(result.page_url, params, result.methods, f);
    //this is only for test
    //get_file_response(TEST_FILE_NAME, f);
    
    //this is only for test
    //in_future.result = {reply: f.result};
    
	//the input to enter this process is result: containing the service params
	//the response content: containing the content to be processed
    f.then( function(future) {
      if(future.result && future.result != "failed")  { //
	    process_content(result.patterns, future.result, values,0);
      }else{//end of if(future.result)
        m_code = 'failed';
      }
      in_future.result = {reply: values, 'code':m_code};
    }); //end of future          
              
  }else{ //not find requested service
    //return empty object
      in_future.result = {reply: null, 'code':'miaomiao'};
  }//end of if result
 }

/*
* process the content
*/
function process_content(patterns, response_content, values, in_index) {
	logInfo(" process_content response_content " + JSON.stringify(response_content));
	//response_content = future.result;
	
	//process the result
	//get the startposition
	var j = 0;
	var index_i = 0;
	
	//start process the content based on patterns in the configuration
	//var patterns = result.patterns;
	
	logInfo("pring the patterns " + JSON.stringify(patterns));
	
	//global pointer in process_content, be flexible for coming advanced patterns
	var org_rs_content = response_content;
	var org_index = in_index;
	
	var m = 0;// index of values
			
	logInfo("about to enter for(j=0..." + patterns.length);
	for(j = 0; j < patterns.length; j++) {
	
	  var k = 0;// the index of pattern---------patterns[j].value_set
	
	  //process block firstly, if patterns.block != block, then don't need to process block function
	  logInfo("enter for(j=");
	  logInfo("j, pring the patterns[j] " + j + ",  " + JSON.stringify(patterns[j]));

	  //if it is the second block, add block value in the list.
	  if(j > 0 && m >0) {
	    var value = {"block":"newblock"};
	    values[m] = value;
		m++;
	  }	  
  
	  if(patterns[j].block == "block") {
	  
	    //check block condition
		if( patterns[j].block_condition ) {
		   var temp_result = find_delimited(org_rs_content, patterns[j].condition_prefix_tag, patterns[j].condition_suffix_tag, org_index);
		   logInfo("----in block conditino " + temp_result.index + "   " + patterns[j].block_condition );
		   if(temp_result.index < 0 || temp_result.result != patterns[j].block_condition ) continue;
		}
	  
		//check whether it is a block information, if so, only process the block
		var temp_result = find_delimited(org_rs_content, patterns[j].prefix_tag, patterns[j].suffix_tag, org_index);
		response_content = temp_result.result;
		org_index = temp_result.index;
		
		//updated index for changed response content,if it is valid
		if(response_content) index_i = 0;
		else index_i = temp_result.index;
	  }//end of pattern.block
	  
	  //start process value set
	  var pattern = patterns[j].value_set;
  
	  
	  //print test information
	  logInfo("response_content :" + JSON.stringify(response_content));
	  logInfo(" pattern information " + JSON.stringify(pattern));
					  
	  
	  while (index_i >= 0) {
	  
		//get value
		var value = {};
		k = 0;//reset to zero to extract from start
		
		//go through pattern one by one
		while ( k < pattern.length && index_i >= 0 ) {
		
		  //for test
		  logInfo(" checking response_content: " + JSON.stringify(response_content.substring( index_i, response_content.length)) );
		  
		  //check whether the pattern in the value set is a block...if so go into block process...recursive
		  /*if(pattern[k].block && pattern[k].block == 'block' ) {
		    var sub_values =[];
		    index_i = process_content(pattern[k], response_content, sub_values, in_index);
			value[pattern[k].attribute_name] = sub_values;
			continue;
		  }*/
		  
		  //check whether the condition matched if the pattern has condition described
		  if(pattern[k].condition_name) {
		    var c_value = pattern[k].condition_value;
			logInfo(" pring condition  " + c_value + "  " + c_value.indexOf('&&') + "-----" + value[pattern[k].condition_name].indexOf( c_value.substring(2,c_value.length)	));
            if ( (c_value.indexOf('&&') == 0 && value[pattern[k].condition_name].indexOf( c_value.substring(2,c_value.length)	) < 0)
                  || (c_value.indexOf('&&') != 0 && value[pattern[k].condition_name] != c_value )) {
				logInfo(" condition not satisfied, skip condidtion " + pattern[k].condition_value );
				k++;
				continue;
			}
		  }
		
		  var temp_result = find_delimited(response_content, pattern[k].prefix_tag, pattern[k].suffix_tag, index_i);
		  
		  var temp_result2 = {result: "", index: -1};
		  var temp_result3 = {result: "", index: -1};
		  
		  logInfo(" has prefix_tag2 ? " + JSON.stringify(pattern[k].prefix_tag2) );
		  
		  //for the different prefix but same data
		  if(pattern[k].prefix_tag2) {
			//if has suffix_tag2, need to pair prefix_tag2 with prefix_tag2
			if(pattern[k].suffix_tag2) {
			  temp_result2 = find_delimited(response_content, pattern[k].prefix_tag2, pattern[k].suffix_tag2, index_i);
			  logInfo(" has suffix_tag2 ? "+JSON.stringify(pattern[k].suffix_tag2) + "   " + JSON.stringify(temp_result2));
			}else{
			  temp_result2 = find_delimited(response_content, pattern[k].prefix_tag2, pattern[k].suffix_tag, index_i);
			}
		  }
		  
		  if(pattern[k].prefix_tag3) {
		    //prefix_tag3 should have suffix_tag3
			temp_result3 = find_delimited(response_content, pattern[k].prefix_tag3, pattern[k].suffix_tag3, index_i);
			//logInfo("  has prefix/suffix_tag3   " + JSON.stringify(temp_result3));
		  }
		  
		  if (temp_result2.index >= 0 && (temp_result2.index < temp_result.index || temp_result.index < 0) ) {
			temp_result = temp_result2;
		  }
		  
		  if (temp_result3.index >=0 && (temp_result3.index < temp_result.index || temp_result.index < 0) ) {
			temp_result = temp_result3;
		  }
		  
		  // if the name is "nouse"
		  if ( pattern[k].attribute_name != "nouse" ) {
			value[pattern[k].attribute_name] = temp_result.result;
		  }
		  index_i = temp_result.index;
		  k++;
		  
		  //for test
		  logInfo(" index_i,k,m and value " + index_i + "," + k + "," + m + " ||| " +  JSON.stringify(value) );
		}//end of while
		
		//valid value set if index_i >= 0
		if(index_i >=0) {
		  values[m] = value;
		  m++;
		}
		
		//if it is not a repeated block, then break
		if( patterns[j].is_repeat == 'no' )break;
		//for test
		logInfo(" index_i,k,m and values " + index_i + "," + k + "," + m + " ||| " +  JSON.stringify(values) );
														  
	  }

	}//end of for(j=0
    return org_index;
}


/*
* call the page service and put response into a file
*/
function callTransServiceR2F(config_obj, in_servicename, in_params, in_controller, in_future)
{
  //get configObj from palm service...
  
  //get pagelist
  var page_list = config_obj.page_list;
  
  logInfo("in_servicename = " +in_servicename + " page_list =" + JSON.stringify(page_list));
  var result_json_string = "{";
  
  var result = null;
  var i = 0;

  while ( i < page_list.length && result == null ) {
    if (page_list[i].service_name == in_servicename ) {
        result = page_list[i];
    }else{
        i++;
    }  
  }//end of while
  
  var values = [];
 
  logInfo("servicename/result " + in_servicename+"/"+JSON.stringify(result));
  
  var response_content = null;
  m_code = 'ok';
  
  //get the link
  if (result != null) { 
    
    //call the web server, use in_future to control the flow
    var f = new Future();
    
    var params = result.params;
	
    for(var attr in in_params){
        params[attr] = in_params[attr];
    }
    
    logInfo( "params = " + JSON.stringify(params) );
   
    //call the url to get response
    get_url_response(result.page_url, params, result.methods, f);
    //this is only for test
    //get_file_response(TEST_FILE_NAME, f);
    
    //this is only for test
    //in_future.result = {reply: f.result};
   

    f.then( function(future) {
      if(future.result && future.result != "failed")  { 
    
            logInfo(" future.result " + JSON.stringify(future.result));
            response_content = future.result;
			
			in_controller.serviceRequest("palm://com.hpit.cdc.pagetrans.service", 
				{
					method: "writefile",
					parameters: {
					             'fname': '/media/internal/response_source_Trans.htm',
					             'fcontent': response_content
								},
					onSuccess:function(successData) {
								future.result = successData;
							   }.bind(this),
					onFailure:function(failureData) {
								future.result = null;
							   }.bind(this)
				});
	    }
	logInfo( "in R2F future.result = " + JSON.stringify(future.result)); 
	  in_future.result = "done";
	})
  }
}